﻿// -----------------------------------------------------------------------
// <copyright file="ImageEnhancement.cs" company="Image Processing Course Project">
// Created 21/10/2011
// </copyright>
// -----------------------------------------------------------------------

namespace ImageProcessing
{
    using FrequencyDomain;
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using ImageProcessing.IntermediateClasses;
    using ImageProcessing.Utilities;
    using MathWorks.MATLAB.NET.Arrays;
    using MathWorks.MATLAB.NET.Utility;


    /// <summary>
    /// Class for image enhancement operations
    /// </summary>
    public class ImageEnhancement
    {
        /// <summary>
        /// Byte array for red values
        /// </summary>
        private byte[,] bufferR;

        /// <summary>
        /// Byte array for green values
        /// </summary>
        private byte[,] bufferG;

        /// <summary>
        /// Byte array for blue values
        /// </summary>
        private byte[,] bufferB;

        /// <summary>
        /// Local Bitmap image for processing
        /// </summary>
        private Bitmap image;

        /// <summary>
        /// Local object from class histogram to use pixel color frequency arrays
        /// </summary>
        private Histogram histogram;

        /// <summary>
        /// Fill byte color arrays
        /// </summary>
        private void FillOriginalImageBuffer()
        {
            try
            {
                this.bufferR = new byte[this.image.Width, this.image.Height];
                this.bufferG = new byte[this.image.Width, this.image.Height];
                this.bufferB = new byte[this.image.Width, this.image.Height];
                for (int i = 0; i < this.image.Height; i++)
                {
                    for (int j = 0; j < this.image.Width; j++)
                    {
                        this.bufferR[j, i] = this.image.GetPixel(j, i).R;
                        this.bufferG[j, i] = this.image.GetPixel(j, i).G;
                        this.bufferB[j, i] = this.image.GetPixel(j, i).B;
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Initializes a new instance of the ImageEnhancement class
        /// </summary>
        /// <param name="img">Currently displayed image</param>
        public ImageEnhancement(Image img)
        {
            this.image = new Bitmap(img);
            this.histogram = new Histogram(img);
        }

        /// <summary>
        /// Image quantization/compression by removing bits from the image color pixels using an 8-bit mask
        /// </summary>
        /// <param name="mask">8-bit mask</param>
        /// <returns>Bitmap image</returns>
        public Bitmap Quantization(int mask)
        {
            try
            {
                string binaryMask = string.Empty;
                for (int i = 0; i < mask; i++)
                {
                    binaryMask += "1";
                }

                for (int i = binaryMask.Length; i < 8; i++)
                {
                    binaryMask += "0";
                }

                int k = 7;
                int value = 0;
                for (int i = 0; i < 8; i++)
                {
                    value += int.Parse(binaryMask[i].ToString()) * (int)Math.Pow(2, k);
                    k--;
                }

                Bitmap newImage = new Bitmap(this.image.Width, this.image.Height);
                int maskedRed, maskedGreen, maskedBlue;
                for (int i = 0; i < newImage.Height; i++)
                {
                    for (int j = 0; j < newImage.Width; j++)
                    {
                        maskedRed = this.image.GetPixel(j, i).R & value;
                        maskedGreen = this.image.GetPixel(j, i).G & value;
                        maskedBlue = this.image.GetPixel(j, i).B & value;
                        newImage.SetPixel(j, i, Color.FromArgb(maskedRed, maskedGreen, maskedBlue));
                    }
                }

                return newImage;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Equalizes Array
        /// </summary>
        /// <param name="image">Image being processed</param>
        /// <param name="color">Color to work on</param>
        /// <returns>Equalized Array</returns>
        public int[] EqualizedArray(Bitmap image, string color)
        {
            int length = 256;
            Histogram histogram = new Histogram(image);
            int[] colorArray = new int[length];
            try
            {
                for (int i = 0; i < length; i++)
                {
                    if (color.Equals("red"))
                    {
                        colorArray[i] = histogram.GetRedArray()[i];
                    }
                    else if (color.Equals("green"))
                    {
                        colorArray[i] = histogram.GetGreenArray()[i];
                    }
                    else
                    {
                        colorArray[i] = histogram.GetBlueArray()[i];
                    }
                }

                for (int i = 1; i < length; i++)
                {
                    colorArray[i] += colorArray[i - 1];
                }

                double temp;
                for (int i = 0; i < length; i++)
                {
                    if (color.Equals("red"))
                    {
                        temp = (double)colorArray[i] / (double)colorArray[255];
                        colorArray[i] = (int)(temp * 255);
                    }
                    else if (color.Equals("green"))
                    {
                        temp = (double)colorArray[i] / (double)colorArray[255];
                        colorArray[i] = (int)(temp * 255);
                    }
                    else
                    {
                        temp = (double)colorArray[i] / (double)colorArray[255];
                        colorArray[i] = (int)(temp * 255);
                    }
                }

                return colorArray;
            }
            catch (Exception ex)
            {
                Utilities.Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Image and histogram equalization
        /// </summary>
        /// <param name="image">Original image</param>
        /// <returns>Bitmap image</returns>
        public Bitmap Equalization(Bitmap image)
        {
            try
            {
                Histogram histogram = new Histogram(image);
                Bitmap newImage = new Bitmap(image.Width, image.Height);
                int[] equalizedRedArray = new int[256];
                int[] equalizedGreenArray = new int[256];
                int[] equalizedBlueArray = new int[256];

                equalizedRedArray = this.EqualizedArray(image, "red");
                equalizedGreenArray = this.EqualizedArray(image, "green");
                equalizedBlueArray = this.EqualizedArray(image, "blue");

                for (int i = 0; i < newImage.Height; i++)
                {
                    for (int j = 0; j < newImage.Width; j++)
                    {
                        newImage.SetPixel(j, i, Color.FromArgb(equalizedRedArray[image.GetPixel(j, i).R], equalizedGreenArray[image.GetPixel(j, i).G], equalizedBlueArray[image.GetPixel(j, i).B]));
                    }
                }

                return newImage;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Image contrast adjustment
        /// </summary>
        /// <param name="newMinumum">New range minimum value</param>
        /// <param name="newMaximum">New range maximum value</param>
        /// <returns>Bitmap image with adjusted contrast</returns>
        public Bitmap Contrast(int newMinumum, int newMaximum)
        {
            try
            {
                Bitmap newImage = new Bitmap(this.image.Width, this.image.Height);

                int[] redArray = this.histogram.GetRedArray();
                int[] greenArray = this.histogram.GetGreenArray();
                int[] blueArray = this.histogram.GetBlueArray();

                int minimumRedValue = 0, minimumGreenValue = 0, minimumBlueValue = 0, maximumRedValue = 255, maximumGreenValue = 255, maximumBlueValue = 255;
                int newRed, newGreen, newBlue;

                for (int i = 0; i < 256; i++)
                {
                    if (redArray[i] != 0)
                    {
                        minimumRedValue = i;
                        break;
                    }
                }

                for (int i = 0; i < 256; i++)
                {
                    if (greenArray[i] != 0)
                    {
                        minimumGreenValue = i;
                        break;
                    }
                }

                for (int i = 0; i < 256; i++)
                {
                    if (blueArray[i] != 0)
                    {
                        minimumBlueValue = i;
                        break;
                    }
                }

                for (int i = 255; i >= 0; i--)
                {
                    if (redArray[i] != 0)
                    {
                        maximumRedValue = i;
                        break;
                    }
                }

                for (int i = 255; i >= 0; i--)
                {
                    if (greenArray[i] != 0)
                    {
                        maximumGreenValue = i;
                        break;
                    }
                }

                for (int i = 255; i >= 0; i--)
                {
                    if (blueArray[i] != 0)
                    {
                        maximumBlueValue = i;
                        break;
                    }
                }

                double temp;
                for (int i = 0; i < this.image.Height; i++)
                {
                    for (int j = 0; j < this.image.Width; j++)
                    {
                        temp = ((double)this.image.GetPixel(j, i).R - (double)minimumRedValue) / ((double)maximumRedValue - (double)minimumRedValue);
                        newRed = (int)((temp * (double)(newMaximum - newMinumum)) + newMinumum);
                        temp = ((double)this.image.GetPixel(j, i).G - (double)minimumGreenValue) / ((double)maximumGreenValue - (double)minimumGreenValue);
                        newGreen = (int)((temp * (double)(newMaximum - newMinumum)) + newMinumum);
                        temp = ((double)this.image.GetPixel(j, i).B - (double)minimumBlueValue) / ((double)maximumBlueValue - (double)minimumBlueValue);
                        newBlue = (int)((temp * (double)(newMaximum - newMinumum)) + newMinumum);

                        if (newRed > 255)
                        {
                            newRed = 255;
                        }
                        else
                            if (newRed < 0)
                            {
                                newRed = 0;
                            }

                        if (newGreen > 255)
                        {
                            newGreen = 255;
                        }
                        else
                            if (newGreen < 0)
                            {
                                newGreen = 0;
                            }

                        if (newBlue > 255)
                        {
                            newBlue = 255;
                        }
                        else
                            if (newBlue < 0)
                            {
                                newBlue = 0;
                            }

                        newImage.SetPixel(j, i, Color.FromArgb(newRed, newGreen, newBlue));
                    }
                }
                Colour.ColorsStruct[,] tempImage;
                tempImage = Colour.ConvertToStruct(newImage);
                return newImage;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Adjust image Brightness
        /// </summary>
        /// <param name="offset">Brightness offset</param>
        /// <returns>Adjusted Bitmap image</returns>
        public Bitmap AdjustBrightness(int offset)
        {
            int newRedValue = 0, newGreenValue = 0, newBlueValue = 0;

            Bitmap originalImage = new Bitmap(this.image);
            Histogram histogram = new Histogram(this.image);
            Bitmap returnImage = new Bitmap(originalImage.Width, originalImage.Height);

            for (int i = 0; i < originalImage.Height; i++)
            {
                for (int j = 0; j < originalImage.Width; j++)
                {
                    newRedValue = originalImage.GetPixel(j, i).R + offset;
                    newGreenValue = originalImage.GetPixel(j, i).G + offset;
                    newBlueValue = originalImage.GetPixel(j, i).B + offset;

                    if (newRedValue > 255)
                    {
                        newRedValue = 255;
                    }
                    else
                        if (newRedValue < 0)
                        {
                            newRedValue = 0;
                        }

                    if (newGreenValue > 255)
                    {
                        newGreenValue = 255;
                    }
                    else
                        if (newGreenValue < 0)
                        {
                            newGreenValue = 0;
                        }

                    if (newBlueValue > 255)
                    {
                        newBlueValue = 255;
                    }
                    else
                        if (newBlueValue < 0)
                        {
                            newBlueValue = 0;
                        }

                    returnImage.SetPixel(j, i, Color.FromArgb(newRedValue, newGreenValue, newBlueValue));
                }
            }

            return returnImage;
        }

        /// <summary>
        /// Gamma Correction Operationa
        /// </summary>
        /// <param name="gamma">Gamma correction value</param>
        /// <returns>Bitmap image</returns>
        public Bitmap Gamma(double gamma)
        {
            try
            {
                double gammaCorrection = gamma;
                Bitmap newImage = new Bitmap(this.image.Width, this.image.Height);
                for (int i = 0; i < this.image.Height; i++)
                {
                    for (int j = 0; j < this.image.Width; j++)
                    {
                        newImage.SetPixel(j, i, Color.FromArgb((int)(255 * Math.Pow(((double)this.image.GetPixel(j, i).R / 255), gammaCorrection)), (int)(255 * Math.Pow(((double)this.image.GetPixel(j, i).G / 255), gammaCorrection)), (int)(255 * Math.Pow(((double)this.image.GetPixel(j, i).B / 255), gammaCorrection))));
                    }
                }

                return newImage;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Matches the histogram of one image to the other
        /// </summary>
        /// <param name="inputImage">Image to be matched to</param>
        /// <param name="oldImage">Image to be matched</param>
        /// <returns>Bitmap Image</returns>
        public Bitmap HistogramMatching(Bitmap inputImage, Bitmap oldImage)
        {
            int length = 256;
            Bitmap resultedImage = new Bitmap(oldImage.Width, oldImage.Height);
            ////Number of pixels of each color in the image to be matched
            int[] equalizedRedArray = new int[length];
            int[] equalizedGreenArray = new int[length];
            int[] equalizedBlueArray = new int[length];
            ////Number of pixels of each color in the user input image used in matching
            int[] equalizedRedArrayInput = new int[length];
            int[] equalizedGreenArrayInput = new int[length];
            int[] equalizedBlueArrayInput = new int[length];
            ////Number of pixels of each color in the resulted image
            int[] equalizedRedArrayResult = new int[length];
            int[] equalizedGreenArrayResult = new int[length];
            int[] equalizedBlueArrayResult = new int[length];

            try
            {
                equalizedRedArray = this.EqualizedArray(oldImage, "red");
                equalizedGreenArray = this.EqualizedArray(oldImage, "green");
                equalizedBlueArray = this.EqualizedArray(oldImage, "blue");

                equalizedRedArrayInput = this.EqualizedArray(inputImage, "red");
                equalizedGreenArrayInput = this.EqualizedArray(inputImage, "green");
                equalizedBlueArrayInput = this.EqualizedArray(inputImage, "blue");

                int tempValue1;
                int tempValue2;
                int difference = 255;
                int tempIndex = -1;
                for (int i = 0; i < length; i++)
                {
                    tempValue1 = equalizedRedArray[i];
                    for (int j = 0; j < length; j++)
                    {
                        tempValue2 = equalizedRedArrayInput[j];
                        if ((tempValue1 - tempValue2) == 0)
                        {
                            tempIndex = j;
                            break;
                        }
                        else if (Math.Abs(tempValue1 - tempValue2) < difference)
                        {
                            difference = Math.Abs(tempValue1 - tempValue2);
                            tempIndex = j;
                        }
                    }

                    equalizedRedArrayResult[i] = tempIndex;
                }

                for (int i = 0; i < length; i++)
                {
                    tempValue1 = equalizedGreenArray[i];
                    for (int j = 0; j < length; j++)
                    {
                        tempValue2 = equalizedGreenArrayInput[j];
                        if ((tempValue1 - tempValue2) == 0)
                        {
                            tempIndex = j;
                            break;
                        }
                        else if (Math.Abs(tempValue1 - tempValue2) < difference)
                        {
                            difference = Math.Abs(tempValue1 - tempValue2);
                            tempIndex = j;
                        }
                    }

                    equalizedGreenArrayResult[i] = tempIndex;
                }

                for (int i = 0; i < length; i++)
                {
                    tempValue1 = equalizedBlueArray[i];
                    for (int j = 0; j < length; j++)
                    {
                        tempValue2 = equalizedBlueArrayInput[j];
                        if ((tempValue1 - tempValue2) == 0)
                        {
                            tempIndex = j;
                            break;
                        }
                        else if (Math.Abs(tempValue1 - tempValue2) < difference)
                        {
                            difference = Math.Abs(tempValue1 - tempValue2);
                            tempIndex = j;
                        }
                    }

                    equalizedBlueArrayResult[i] = tempIndex;
                }

                for (int i = 0; i < oldImage.Height; i++)
                {
                    for (int j = 0; j < oldImage.Width; j++)
                    {
                        resultedImage.SetPixel(j, i, Color.FromArgb(equalizedRedArrayResult[this.image.GetPixel(j, i).R], equalizedGreenArrayResult[this.image.GetPixel(j, i).G], equalizedBlueArrayResult[this.image.GetPixel(j, i).B]));
                    }
                }

                return resultedImage;
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                throw;
            }
        }

        /// <summary>
        /// Enhance image using specific statistics
        /// </summary>
        /// <param name="statistics">Statistic values</param>
        /// <param name="winSize">Window size</param>
        /// <returns>Bitmap image</returns>
        public Bitmap LocalEnhancement(List<double> statistics, int winSize)
        {
            FourierTransformClass enhancement = new FourierTransformClass();
            double k0 = statistics[0];
            double k1 = statistics[1];
            double k2 = statistics[2];
            double e = statistics[3];
            this.FillOriginalImageBuffer();
            MWNumericArray tempR = (MWNumericArray)enhancement.LocalEnhancementStatistics((MWNumericArray)this.bufferR, k0, k1, k2, e, winSize);
            MWNumericArray tempG = (MWNumericArray)enhancement.LocalEnhancementStatistics((MWNumericArray)this.bufferG, k0, k1, k2, e, winSize);
            MWNumericArray tempB = (MWNumericArray)enhancement.LocalEnhancementStatistics((MWNumericArray)this.bufferB, k0, k1, k2, e, winSize);

            byte[,] resultedR = (byte[,])tempR.ToArray();
            byte[,] resultedG = (byte[,])tempG.ToArray();
            byte[,] resultedB = (byte[,])tempB.ToArray();

            for (int i = 0; i < this.image.Height; i++)
            {
                for (int j = 0; j < this.image.Width; j++)
                {
                    this.image.SetPixel(j,i,Color.FromArgb((int)resultedR[j, i], (int)resultedG[j, i],(int)resultedB[j, i]));
                }
            }
            return image;
        }

        public Bitmap LocalEqualization(int winSize)
        {
            FourierTransformClass enhancement = new FourierTransformClass();
            this.FillOriginalImageBuffer();
            MWNumericArray tempR = (MWNumericArray)enhancement.LocalEqualization((MWNumericArray)this.bufferR, winSize);
            MWNumericArray tempG = (MWNumericArray)enhancement.LocalEqualization((MWNumericArray)this.bufferG, winSize);
            MWNumericArray tempB = (MWNumericArray)enhancement.LocalEqualization((MWNumericArray)this.bufferB, winSize);

            byte[,] resultedR = (byte[,])tempR.ToArray();
            byte[,] resultedG = (byte[,])tempG.ToArray();
            byte[,] resultedB = (byte[,])tempB.ToArray();

            for (int i = 0; i < this.image.Height; i++)
            {
                for (int j = 0; j < this.image.Width; j++)
                {
                    this.image.SetPixel(j, i, Color.FromArgb((int)resultedR[j, i], (int)resultedG[j, i], (int)resultedB[j, i]));
                }
            }
            return image;
        }
    }
}
